Skip to content

Introduce Scope::NonGlobModule and Scope::GlobModule #144131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

b-naber
Copy link
Contributor

@b-naber b-naber commented Jul 18, 2025

Splits Scope::Module into Scope::NonGlobModule and Scope::GlobModule. This makes it easier to implement the open namespaces project, where we have to introduce a third scope. Introducing two separate scopes for a module was also a change requested by @petrochenkov, and should make it easier to output better diagnostics.

r? @petrochenkov

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 18, 2025
@rust-log-analyzer

This comment has been minimized.

fmease added a commit to fmease/rust that referenced this pull request Jul 24, 2025
resolve: Remove `Scope::CrateRoot`

Use `Scope::Module` with the crate root module inside instead, which should be identical.
This is a simplification by itself, but it will be even larger simplification if something like rust-lang#144131 is implemented, because `Scope::CrateRoot` is also a module with two actual scopes in it (for globs and non-globs).

I also did some renamings for consistency:
- `ScopeSet::AbsolutePath` -> `ScopeSet::ModuleAndExternPrelude`
- `ModuleOrUniformRoot::CrateRootAndExternPrelude` -> `ModuleOrUniformRoot::ModuleAndExternPrelude`
- `is_absolute_path` -> `module_and_extern_prelude`
fmease added a commit to fmease/rust that referenced this pull request Jul 25, 2025
resolve: Remove `Scope::CrateRoot`

Use `Scope::Module` with the crate root module inside instead, which should be identical.
This is a simplification by itself, but it will be even larger simplification if something like rust-lang#144131 is implemented, because `Scope::CrateRoot` is also a module with two actual scopes in it (for globs and non-globs).

I also did some renamings for consistency:
- `ScopeSet::AbsolutePath` -> `ScopeSet::ModuleAndExternPrelude`
- `ModuleOrUniformRoot::CrateRootAndExternPrelude` -> `ModuleOrUniformRoot::ModuleAndExternPrelude`
- `is_absolute_path` -> `module_and_extern_prelude`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jul 25, 2025
resolve: Remove `Scope::CrateRoot`

Use `Scope::Module` with the crate root module inside instead, which should be identical.
This is a simplification by itself, but it will be even larger simplification if something like rust-lang#144131 is implemented, because `Scope::CrateRoot` is also a module with two actual scopes in it (for globs and non-globs).

I also did some renamings for consistency:
- `ScopeSet::AbsolutePath` -> `ScopeSet::ModuleAndExternPrelude`
- `ModuleOrUniformRoot::CrateRootAndExternPrelude` -> `ModuleOrUniformRoot::ModuleAndExternPrelude`
- `is_absolute_path` -> `module_and_extern_prelude`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jul 25, 2025
resolve: Remove `Scope::CrateRoot`

Use `Scope::Module` with the crate root module inside instead, which should be identical.
This is a simplification by itself, but it will be even larger simplification if something like rust-lang#144131 is implemented, because `Scope::CrateRoot` is also a module with two actual scopes in it (for globs and non-globs).

I also did some renamings for consistency:
- `ScopeSet::AbsolutePath` -> `ScopeSet::ModuleAndExternPrelude`
- `ModuleOrUniformRoot::CrateRootAndExternPrelude` -> `ModuleOrUniformRoot::ModuleAndExternPrelude`
- `is_absolute_path` -> `module_and_extern_prelude`
rust-timer added a commit that referenced this pull request Jul 25, 2025
Rollup merge of #144368 - petrochenkov:rmrootscope, r=b-naber

resolve: Remove `Scope::CrateRoot`

Use `Scope::Module` with the crate root module inside instead, which should be identical.
This is a simplification by itself, but it will be even larger simplification if something like #144131 is implemented, because `Scope::CrateRoot` is also a module with two actual scopes in it (for globs and non-globs).

I also did some renamings for consistency:
- `ScopeSet::AbsolutePath` -> `ScopeSet::ModuleAndExternPrelude`
- `ModuleOrUniformRoot::CrateRootAndExternPrelude` -> `ModuleOrUniformRoot::ModuleAndExternPrelude`
- `is_absolute_path` -> `module_and_extern_prelude`
@bors

This comment was marked as resolved.

github-actions bot pushed a commit to rust-lang/rustc-dev-guide that referenced this pull request Jul 28, 2025
resolve: Remove `Scope::CrateRoot`

Use `Scope::Module` with the crate root module inside instead, which should be identical.
This is a simplification by itself, but it will be even larger simplification if something like rust-lang/rust#144131 is implemented, because `Scope::CrateRoot` is also a module with two actual scopes in it (for globs and non-globs).

I also did some renamings for consistency:
- `ScopeSet::AbsolutePath` -> `ScopeSet::ModuleAndExternPrelude`
- `ModuleOrUniformRoot::CrateRootAndExternPrelude` -> `ModuleOrUniformRoot::ModuleAndExternPrelude`
- `is_absolute_path` -> `module_and_extern_prelude`
@b-naber
Copy link
Contributor Author

b-naber commented Aug 1, 2025

@petrochenkov do you have time to review this in the next couple of days?

@petrochenkov
Copy link
Contributor

Probably after #144579, this is low priority for me.

@b-naber
Copy link
Contributor Author

b-naber commented Aug 1, 2025

I figured, I'll just re-roll then.

r? @rust-lang/compiler

@rustbot rustbot assigned SparrowLii and unassigned petrochenkov Aug 1, 2025
@petrochenkov petrochenkov self-assigned this Aug 1, 2025
@petrochenkov
Copy link
Contributor

I still need to look, this is an important part of the resolve infrastructure.

@b-naber
Copy link
Contributor Author

b-naber commented Aug 1, 2025

Other maintainers are also qualified to review this code. PR writers are allowed to re-roll reviewers after 2 weeks of inactivity; the fact that you just ignore this is kind of wild.

@Kobzol
Copy link
Member

Kobzol commented Aug 1, 2025

Vadim is the foremost expert on name resolution in the compiler, and if he wants to take a look at the PR, that is completely fine. No one is taking away your option of rerolling, but that doesn't mean that you get to choose the only reviewer. If there are other people with concerns or comments, their input also has to be taken into account, regardless of whether they are the currently assigned reviewer or not.

@RalfJung
Copy link
Member

RalfJung commented Aug 1, 2025

(Btw you just pinged the entire compiler team, more than 50 people. The way to re-roll is r? compiler, then it only pings the chosen reviewer. But even that is unlikely to work well since it doesn't take into account expertise, unlike the initial assignment.)

@b-naber
Copy link
Contributor Author

b-naber commented Aug 1, 2025

Vadim is the foremost expert on name resolution in the compiler, and if he wants to take a look at the PR, that is completely fine. No one is taking away your option of rerolling, but that doesn't mean that you get to choose the only reviewer. If there are other people with concerns or comments, their input also has to be taken into account, regardless of whether they are the currently assigned reviewer or not.

I understand, but there must be some limit for how long someone can request to take a look at a PR; if the PR has low priority for the maintainer requesting a review, at some point there should still be a way to get this merged in a reasonable amount of time. Now obviously 2 weeks isn't anywhere close to such a limit, so I apologize for overreacting here.

@petrochenkov
Copy link
Contributor

I'm certainly going to review this in some time.
I've been doing some experiments in #144368 and https://github.com/petrochenkov/rust/tree/extprel3 to understand how to do this better.
The second branch in particular, once finished, will give a simplified example of what this change should look like eventually.

@petrochenkov
Copy link
Contributor

#144793 splits extern prelude into two scopes and demonstrates some things that need to be done for the module scope split as well.

  • Introduction of ScopeSet::ExternPrelude for doing searches just in the two extern prelude scopes, and its use in resolve_ident_in_module_unadjusted. ScopeSet::Module should also be introduced and behave similarly.
  • Replacement of the ad hoc MacroExpandedExternCrateCannotShadowExternArguments error with a regular restricted shadowing error (AmbiguityKind::MoreExpandedVsOuter), slightly specialized to produce better diagnostics.

non_glob_module_inserted = true;
}
Scope::GlobModule(module, _) => {
if !non_glob_module_inserted {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non_glob_module_inserted will always be true as written.

self.visit_scopes(ScopeSet::All(TypeNS), parent_scope, ctxt, |this, scope, _, _| {
match scope {
Scope::Module(module, _) => {
Scope::NonGlobModule(module, _) => {
this.traits_in_module(module, assoc_item, &mut found_traits);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting case from the language point of view.

Typically shadowed traits do not stop being in scope.
E.g. this works

trait Tr {
    fn method(&self) {}
}

impl Tr for () {}

fn main() {
    trait Tr {} // no method

    ().method(); // the first `Tr` is still in scope despite being shadowed
}

But if a trait from a glob shadowed by something non-glob, then it is no longer a scope.

Seems like a bug, but not sure if we'll be able to fix it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a specific test:

mod glob {
    pub trait Tr {
        fn method(&self) {}
    }
    impl Tr for () {}
}

// a: use glob::*; // ok
fn main() {
    // b: use glob::*; // error
    trait Tr {} // no method
    ().method();
}

@@ -1076,7 +1076,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
}
}
Scope::Module(module, _) => {
Scope::NonGlobModule(module, _) | Scope::GlobModule(module, _) => {
this.add_module_candidates(module, &mut suggestions, filter_fn, None);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to add candidates twice, Scope::GlobModule can be skipped like in #144793.

@@ -1487,9 +1487,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
&parent_scope,
ident.span.ctxt(),
|this, scope, _use_prelude, _ctxt| {
let Scope::Module(m, _) = scope else {
return None;
let m = match scope {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let m = match scope {
let Scope::Module(m, _) | Scope::GlobModule(module, _) = scope else {

@b-naber
Copy link
Contributor Author

b-naber commented Aug 5, 2025

Thanks for the review, I'll address it tomorrow.

I have a question about ScopeSet::Module though. The idea is to just have a call to early_resolve_ident_in_lexical_scope for the ModuleOrUniformRoot::Module variant, right? We still need to return Weak in resolve_ident_in_module_unadjusted though, I believe, and the early_resolve_ident_in_lexical_scope call discards that information. How do you want to get the value for Weak here? Change the return type of early_resolve_ident_in_lexical_scope? Otherwise calculating the correct variant would require us to replicate a lot of the logic of resolve_ident_in_non_glob_module and resolve_ident_in_glob_module.

@petrochenkov
Copy link
Contributor

Sorry, I had to go and didn't finish the review, I'll review the remaining part tomorrow.

The idea is to just have a call to early_resolve_ident_in_lexical_scope for the ModuleOrUniformRoot::Module variant, right?

Yeah, the main missing part is ScopeSet::Module and using it for resolving in ModuleOrUniformRoot::Module.
I didn't yet look in detail how Weak should be treated.
If resolve_ident_in_module_unadjusted is no longer called from early_resolve_ident_in_lexical_scope, then maybe it won't need to return Weak at all, I'm not sure.

struct Flags: u8 {
const MACRO_RULES = 1 << 0;
const NON_GLOB_MODULE = 1 << 1;
const GLOB_MODULE = 1 << 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The single MODULE flag could be kept, the new flags are used identically.

TypeNS => {
ctxt.adjust(ExpnId::root());
Scope::ExternPrelude
Scope::NonGlobModule(..) | Scope::GlobModule(..) if module_and_extern_prelude => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Scope::NonGlobModule(..) | Scope::GlobModule(..) if module_and_extern_prelude => {
Scope::GlobModule(..) if module_and_extern_prelude => {

From non-glob module we go to glob module, not to extern prelude.

@petrochenkov petrochenkov added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants